<template>
    <div class="executions">
        <p
            class="code"
            v-for="(execution, index) in executions"
            :key="index"
            v-show="execution.visible"
        >
            <span class="addon">~</span>
            [ <span class="time">{{ execution.time }}</span> ]
            <span class="task">{{ execution.name }}</span>
            <span class="duration"> {{ execution.duration }} ms </span>
        </p>
        <!--进度条-->
        <p class="code" v-show="executionInfo.isProcessed">
            <span class="addon">~</span>
            {{ executionInfo.progressBarText }}
            <span class="percentage">{{ percentage }}%</span>
        </p>
        <!--执行命令-->
        <p class="code" v-show="executionEnd.visible">
            <span class="addon">~</span>
            [ <span class="time">{{ executionEnd.time }}</span> ]
            <span class="task">{{ executionEnd.name }}</span>
        </p>
    </div>
</template>

<script setup>
import {
    defineProps,
    reactive,
    ref,
    computed,
    watch,
    defineEmits,
    onUpdated,
} from "vue";
import { executions as ex } from "../mock/data";
// 传递属性
const props = defineProps({
    canExecute: {
        type: Boolean,
        default: true,
    },
});
const executions = ref(ex);
// 执行对象
const executionInfo = reactive({
    isProcessed: false, // 是否进度条
    isFinished: false,
    progressBarText: "--------------------------",
});
const executionEnd = reactive({
    name: "打开中...",
    time: "",
    visible: false,
});
// 进度百分比
const percentage = computed(() => {
    const hashL = executionInfo.progressBarText
        .split("")
        .filter((c) => c === "#").length;
    const l = executionInfo.progressBarText.length;
    return Math.floor((hashL * 100) / l);
});
// 执行命令
const progressivelyRun = (execution, duration = Math.random() * 50 + 250) => {
    return new Promise((resolve) => {
        let showCode = () => {
            execution.time = new Date().toLocaleTimeString();
            execution.duration = duration.toFixed(2);
            execution.visible = true;
            resolve();
        };
        setTimeout(showCode, duration);
    });
};
// 显示进度条
const successProcessing = (duration) => {
    return new Promise((resolve) => {
        let start = null,
            progressing,
            progressingCount = 0;
        executionInfo.isProcessed = true;
        let step = (timestamp) => {
            let timeGap;
            if (start === null) {
                start = timestamp;
            }
            timeGap = timestamp - start;
            if (progressingCount % 3 === 0) {
                executionInfo.progressBarText =
                    executionInfo.progressBarText.replace("-", "#");
            }
            progressingCount++;
            if (timeGap < duration) {
                progressing = requestAnimationFrame(step);
            } else {
                resolve();
                executionInfo.progressBarText =
                    executionInfo.progressBarText.replace(/-/g, "#");
                cancelAnimationFrame(progressing);
            }
        };
        progressing = requestAnimationFrame(step);
    });
};
const $emit = defineEmits(["onUpdating", "onFinish"]);
// 执行方法
const runExecutions = async () => {
    // 执行每一条命令
    for (const execution of executions.value) {
        await progressivelyRun(execution);
    }
    // 执行完命令，开始显示进度条
    await successProcessing(1000);
    // 执行最后一条命令
    await progressivelyRun(executionEnd).then(() => {
        setTimeout(() => {
            executionInfo.isFinished = true; // 标志执行完成
            $emit("onFinish"); // 发生完成事件通知父组件
        }, 500);
    });
};
// 更新时触发
onUpdated(() => {
    $emit("onUpdating");
});
// 监听父组件属性
watch(
    () => props.canExecute,
    (next) => {
        next && runExecutions();
    }
);
</script>

<style lang="less">
.executions {
    .addon {
        color: #68fcfb;
    }
    .time {
        color: #666;
    }
    .task {
        color: #009ab2;
    }
    .duration {
        margin-left: 10px;
        color: #bf36b7;
    }
}
</style>
